package com.caipos.actor.api;

import com.caipos.actor.common.data.id.EntityId;
import com.caipos.actor.common.data.id.RuleChainId;
import com.caipos.actor.common.data.id.RuleNodeId;
import com.caipos.actor.common.data.rule.RuleNode;
import com.caipos.actor.common.data.id.TenantId;
import com.caipos.actor.common.data.msg.KbMsgType;
import com.caipos.actor.common.data.rule.RuleNodeState;
import com.caipos.actor.common.msg.KbMsg;
import com.caipos.actor.common.msg.KbMsgMetaData;
import com.caipos.service.RuleChainService;
import com.caipos.service.TenantService;
import com.caipos.util.ListeningExecutor;
import io.netty.channel.EventLoopGroup;

import javax.management.relation.RelationService;
import javax.script.ScriptEngine;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

public interface KbContext {

    /*
     *
     *  METHODS TO CONTROL THE MESSAGE FLOW
     *
     */

    /**
     * Indicates that message was successfully processed by the rule node.
     * Sends message to all Rule Nodes in the Rule Chain
     * that are connected to the current Rule Node using "Success" relationType.
     *
     * @param msg
     */
    void tellSuccess(KbMsg msg);

    /**
     * Sends message to all Rule Nodes in the Rule Chain
     * that are connected to the current Rule Node using specified relationType.
     *
     * @param msg
     * @param relationType
     */
    void tellNext(KbMsg msg, String relationType);

    /**
     * Sends message to all Rule Nodes in the Rule Chain
     * that are connected to the current Rule Node using one of specified relationTypes.
     *
     * @param msg
     * @param relationTypes
     */
    void tellNext(KbMsg msg, Set<String> relationTypes);

    /**
     * Sends message to the current Rule Node with specified delay in milliseconds.
     * Note: this message is not queued and may be lost in case of a server restart.
     *
     * @param msg
     */
    void tellSelf(KbMsg msg, long delayMs);

    /**
     * Notifies Rule Engine about failure to process current message.
     *
     * @param msg - message
     * @param th  - exception
     */
    void tellFailure(KbMsg msg, Throwable th);

    /**
     * Puts new message to queue for processing by the Root Rule Chain
     *
     * @param msg - message
     */
    void enqueue(KbMsg msg, Runnable onSuccess, Consumer<Throwable> onFailure);

    /**
     * Sends message to the nested rule chain.
     * Fails processing of the message if the nested rule chain is not found.
     *
     * @param msg - the message
     * @param ruleChainId - the id of a nested rule chain
     */
    void input(KbMsg msg, RuleChainId ruleChainId);

    /**
     * Sends message to the caller rule chain.
     * Acknowledge the message if no caller rule chain is present in processing stack
     *
     * @param msg - the message
     * @param relationType - the relation type that will be used to route messages in the caller rule chain
     */
    void output(KbMsg msg, String relationType);

    /**
     * Puts new message to custom queue for processing
     *
     * @param msg - message
     */
    void enqueue(KbMsg msg, String queueName, Runnable onSuccess, Consumer<Throwable> onFailure);

    void enqueueForTellFailure(KbMsg msg, String failureMessage);

    void enqueueForTellFailure(KbMsg kbMsg, Throwable t);

    void enqueueForTellNext(KbMsg msg, String relationType);

    void enqueueForTellNext(KbMsg msg, Set<String> relationTypes);

    void enqueueForTellNext(KbMsg msg, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure);

    void enqueueForTellNext(KbMsg msg, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure);

    void enqueueForTellNext(KbMsg msg, String queueName, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure);

    void enqueueForTellNext(KbMsg msg, String queueName, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure);

    void ack(KbMsg kbMsg);

    KbMsg newMsg(String queueName, KbMsgType type, EntityId originator, KbMsgMetaData metaData, String data);

    KbMsg transformMsg(KbMsg origMsg, KbMsgType type, EntityId originator, KbMsgMetaData metaData, String data);

    KbMsg transformMsg(KbMsg origMsg, KbMsgMetaData metaData, String data);

    KbMsg transformMsgOriginator(KbMsg origMsg, EntityId originator);




    KbMsg attributesDeletedActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, List<String> keys);

    /*
     *
     *  METHODS TO PROCESS THE MESSAGES
     *
     */

    void schedule(Runnable runnable, long delay, TimeUnit timeUnit);


    boolean isLocalEntity(EntityId entityId);

    RuleNodeId getSelfId();

    RuleNode getSelf();

    String getRuleChainName();

    String getQueueName();

    TenantId getTenantId();


    TenantService getTenantService();


    String getDeviceStateNodeRateLimitConfig();

    RuleChainService getRuleChainService();


    RelationService getRelationService();


    boolean isExternalNodeForceAck();

    @Deprecated
    ScriptEngine createJsScriptEngine(String script, String... argNames);

    void logJsEvalRequest();

    void logJsEvalResponse();

    void logJsEvalFailure();

    String getServiceId();

    EventLoopGroup getSharedEventLoop();


    RuleNodeState findRuleNodeStateForEntity(EntityId entityId);

    void removeRuleNodeStateForEntity(EntityId entityId);

    RuleNodeState saveRuleNodeState(RuleNodeState state);

    void clearRuleNodeStates();


    void removeListeners();

    ListeningExecutor getExternalCallExecutor();

}
